package com.props.starter.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.props.starter.config.PropsManagementProperties;
import com.props.starter.model.ConfigFile;
import com.props.starter.model.ConfigVersion;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class ConfigFileService {

    private final PropsManagementProperties properties;
    private final PathMatchingResourcePatternResolver resolver;
    private final ObjectMapper objectMapper;
    private final Yaml yaml;
    private final VersionService versionService;
    private ConfigurableEnvironment environment;

    // 定义启动时配置项前缀/关键字（不可运行时修改）
    private static final Set<String> STARTUP_CONFIG_PREFIXES = Set.of(
        "server.port",
        "server.address", 
        "server.ssl",
        "spring.datasource.url",
        "spring.datasource.username", 
        "spring.datasource.password",
        "spring.datasource.driver-class-name",
        "spring.jpa.database-platform",
        "spring.jpa.hibernate.ddl-auto",
        "spring.profiles.active",
        "spring.profiles.include",
        "spring.application.name",
        "spring.config.import",
        "spring.config.location",
        "management.server.port",
        "management.endpoints.web.base-path",
        "logging.config",
        "logging.file.name",
        "logging.file.path"
    );

    // 定义启动时配置项模式匹配
    private static final Set<String> STARTUP_CONFIG_PATTERNS = Set.of(
        "spring.cloud.config",
        "spring.cloud.nacos", 
        "spring.redis.host",
        "spring.redis.port",
        "spring.kafka.bootstrap-servers",
        "spring.rabbitmq.host",
        "spring.rabbitmq.port",
        "eureka.client.service-url"
    );

    public ConfigFileService(PropsManagementProperties properties, VersionService versionService) {
        this.properties = properties;
        this.resolver = new PathMatchingResourcePatternResolver();
        this.objectMapper = new ObjectMapper();
        this.yaml = new Yaml();
        this.versionService = versionService;
    }

    public void setEnvironment(ConfigurableEnvironment environment) {
        this.environment = environment;
    }

    /**
     * 获取所有配置文件列表
     */
    public List<ConfigFile> getAllConfigFiles() {
        List<ConfigFile> configFiles = new ArrayList<>();
        
        for (String scanPath : properties.getScanPaths()) {
            try {
                // 扫描properties文件
                Resource[] propertiesResources = resolver.getResources("classpath*:" + scanPath + "*.properties");
                for (Resource resource : propertiesResources) {
                    if (!shouldExcludeFile(resource.getFilename())) {
                        configFiles.add(createConfigFile(resource, "properties"));
                    }
                }
                
                // 扫描yaml文件
                Resource[] yamlResources = resolver.getResources("classpath*:" + scanPath + "*.yml");
                for (Resource resource : yamlResources) {
                    if (!shouldExcludeFile(resource.getFilename())) {
                        configFiles.add(createConfigFile(resource, "yaml"));
                    }
                }
                
                Resource[] yamlResources2 = resolver.getResources("classpath*:" + scanPath + "*.yaml");
                for (Resource resource : yamlResources2) {
                    if (!shouldExcludeFile(resource.getFilename())) {
                        configFiles.add(createConfigFile(resource, "yaml"));
                    }
                }
                
            } catch (Exception e) {
                log.error("Error scanning config files in path: " + scanPath, e);
            }
        }
        
        // 记录扫描结果
        log.info("Scanned {} config files after exclusion filters", configFiles.size());
        if (properties.getExcludeFiles().length > 0 || 
            properties.getExcludePrefixes().length > 0 || 
            properties.getExcludeSuffixes().length > 0) {
            log.info("Exclusion filters applied - Files: {}, Prefixes: {}, Suffixes: {}", 
                    String.join(", ", properties.getExcludeFiles()),
                    String.join(", ", properties.getExcludePrefixes()),
                    String.join(", ", properties.getExcludeSuffixes()));
        }
        
        return configFiles;
    }

    /**
     * 根据文件名获取配置文件详情
     */
    public ConfigFile getConfigFileDetail(String fileName) {
        try {
            // 首先尝试从所有扫描路径中查找文件
            for (String scanPath : properties.getScanPaths()) {
                String resourcePath = "classpath:" + scanPath + fileName;
                Resource resource = resolver.getResource(resourcePath);
                
                if (resource.exists()) {
                    String fileType = fileName.endsWith(".properties") ? "properties" : "yaml";
                    return createConfigFile(resource, fileType);
                }
            }
            
            // 如果在扫描路径中没找到，尝试直接根路径
            Resource resource = resolver.getResource("classpath:" + fileName);
            if (resource.exists()) {
                String fileType = fileName.endsWith(".properties") ? "properties" : "yaml";
                return createConfigFile(resource, fileType);
            }
            
        } catch (Exception e) {
            log.error("Error getting config file detail: " + fileName, e);
        }
        return null;
    }

    /**
     * 更新配置值
     */
    public boolean updateConfigValue(String fileName, String key, String value) {
        if (!properties.isAllowOnlineEdit()) {
            log.warn("Online edit is disabled");
            return false;
        }

        // 检查配置项是否可编辑
        if (!isConfigEditable(key)) {
            log.warn("Config key '{}' is not editable at runtime (startup configuration)", key);
            return false;
        }

        try {
            // 更新到Spring Environment中
            if (environment != null) {
                MutablePropertySources propertySources = environment.getPropertySources();
                
                // 创建一个新的PropertySource来覆盖原有值
                Map<String, Object> updateMap = new HashMap<>();
                updateMap.put(key, value);
                
                String sourceName = "propsManagementUpdates-" + fileName;
                propertySources.addFirst(new MapPropertySource(sourceName, updateMap));
                
                log.info("Updated config value: {} = {} in file: {}", key, value, fileName);
                return true;
            }
        } catch (Exception e) {
            log.error("Error updating config value: " + key + " in file: " + fileName, e);
        }
        
        return false;
    }

    private ConfigFile createConfigFile(Resource resource, String fileType) throws IOException {
        ConfigFile configFile = new ConfigFile();
        configFile.setFileName(resource.getFilename());
        configFile.setFilePath(resource.getURI().toString());
        configFile.setFileType(fileType);
        configFile.setLastModified(LocalDateTime.now());
        
        try {
            configFile.setFileSize(resource.contentLength());
        } catch (IOException e) {
            configFile.setFileSize(0);
        }
        
        // 读取文件内容
        try (InputStream inputStream = resource.getInputStream()) {
            String content = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            configFile.setRawContent(content);
            
            // 解析配置内容
            Map<String, Object> parsedContent = parseConfigContent(content, fileType);
            
            // 如果有Environment，使用运行时的配置值（包含动态修改的值）
            if (environment != null) {
                Map<String, Object> runtimeContent = new LinkedHashMap<>();
                for (String key : parsedContent.keySet()) {
                    // 获取运行时的实际值，如果没有则使用文件中的值
                    String runtimeValue = environment.getProperty(key);
                    runtimeContent.put(key, runtimeValue != null ? runtimeValue : parsedContent.get(key));
                }
                configFile.setContent(runtimeContent);
            } else {
                configFile.setContent(parsedContent);
            }
            
            // 设置配置项可编辑性标识
            Map<String, Boolean> editableFlags = new HashMap<>();
            for (String key : parsedContent.keySet()) {
                editableFlags.put(key, isConfigEditable(key));
            }
            configFile.setEditableFlags(editableFlags);
            
            // 获取最新版本的内容用于比对修改状态
            Map<String, Object> lastVersionContent = getLastVersionContent(configFile.getFileName());
            configFile.setLastVersionContent(lastVersionContent);
        }
        
        return configFile;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> parseConfigContent(String content, String fileType) {
        Map<String, Object> result = new LinkedHashMap<>();
        
        try {
            if ("properties".equals(fileType)) {
                Properties props = new Properties();
                // 使用 StringReader 确保 UTF-8 编码的字符串正确解析
                try (StringReader reader = new StringReader(content)) {
                    props.load(reader);
                }
                
                // 遍历所有属性，确保 UTF-8 字符正确处理
                for (String key : props.stringPropertyNames()) {
                    String value = props.getProperty(key);
                    // 确保值的 UTF-8 编码正确
                    result.put(key, value);
                }
                
                log.debug("Parsed {} properties from {} file with UTF-8 encoding", result.size(), fileType);
            } else if ("yaml".equals(fileType)) {
                Object yamlObject = yaml.load(content);
                if (yamlObject instanceof Map) {
                    result = flattenYamlMap((Map<String, Object>) yamlObject, "");
                }
                log.debug("Parsed {} properties from {} file", result.size(), fileType);
            }
        } catch (Exception e) {
            log.error("Error parsing {} config content: {}", fileType, e.getMessage(), e);
        }
        
        return result;
    }

    private Map<String, Object> flattenYamlMap(Map<String, Object> map, String prefix) {
        Map<String, Object> result = new LinkedHashMap<>();
        
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey();
            Object value = entry.getValue();
            
            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nestedMap = (Map<String, Object>) value;
                result.putAll(flattenYamlMap(nestedMap, key));
            } else {
                result.put(key, value);
            }
        }
        
        return result;
    }

    /**
     * 判断配置项是否可以在运行时编辑
     * @param configKey 配置项key
     * @return true-可编辑，false-不可编辑（启动时配置）
     */
    private boolean isConfigEditable(String configKey) {
        // 检查精确匹配的启动时配置
        if (STARTUP_CONFIG_PREFIXES.contains(configKey)) {
            return false;
        }
        
        // 检查模式匹配的启动时配置
        for (String pattern : STARTUP_CONFIG_PATTERNS) {
            if (configKey.startsWith(pattern)) {
                return false;
            }
        }
        
        // 默认认为是运行时可编辑的配置
        return true;
    }

    /**
     * 判断文件是否应该被排除扫描
     * @param fileName 文件名
     * @return true-应该排除，false-不排除
     */
    private boolean shouldExcludeFile(String fileName) {
        if (fileName == null || fileName.trim().isEmpty()) {
            return true;
        }

        // 检查精确匹配和通配符模式
        for (String excludePattern : properties.getExcludeFiles()) {
            if (matchesPattern(fileName, excludePattern)) {
                log.debug("File '{}' excluded by pattern: {}", fileName, excludePattern);
                return true;
            }
        }

        // 检查前缀匹配
        for (String prefix : properties.getExcludePrefixes()) {
            if (fileName.startsWith(prefix)) {
                log.debug("File '{}' excluded by prefix: {}", fileName, prefix);
                return true;
            }
        }

        // 检查后缀匹配
        for (String suffix : properties.getExcludeSuffixes()) {
            if (fileName.endsWith(suffix)) {
                log.debug("File '{}' excluded by suffix: {}", fileName, suffix);
                return true;
            }
        }

        return false;
    }

    /**
     * 通配符模式匹配
     * 支持 * (匹配任意字符) 和 ? (匹配单个字符)
     */
    private boolean matchesPattern(String text, String pattern) {
        if (pattern == null || text == null) {
            return false;
        }

        // 如果没有通配符，进行精确匹配
        if (!pattern.contains("*") && !pattern.contains("?")) {
            return text.equals(pattern);
        }

        // 转换为正则表达式
        String regex = pattern
                .replace(".", "\\.")  // 转义点号
                .replace("*", ".*")   // * 匹配任意字符
                .replace("?", ".");   // ? 匹配单个字符

        return text.matches(regex);
    }

    /**
     * 获取最新版本的配置内容
     */
    private Map<String, Object> getLastVersionContent(String fileName) {
        try {
            // 获取最新版本
            List<ConfigVersion> versions = versionService.getVersions(fileName);
            if (versions.isEmpty()) {
                return null;
            }
            
            // 获取最新版本的内容
            ConfigVersion latestVersion = versions.get(0); // versions已经按时间倒序排列
            Map<String, String> versionContent = versionService.getVersionContent(latestVersion.getVersionId());
            
            if (versionContent != null) {
                // 转换为Object类型的Map以保持一致性
                Map<String, Object> result = new LinkedHashMap<>();
                for (Map.Entry<String, String> entry : versionContent.entrySet()) {
                    result.put(entry.getKey(), entry.getValue());
                }
                return result;
            }
        } catch (Exception e) {
            log.debug("No version found for file: " + fileName);
        }
        return null;
    }
} 